/*
 * Copyright (c) 2010-2013 Ruben Chakhmakhchyan. All rights reserved.
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Eclipse Public License
 * Version 1.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://opensource.org/licenses/EPL-1.0 and read it before using this file.
 */

#include "url.h"
#include <stdlib.h>
#include <string.h>
#include "commontypes.h"
#include "convert.h"
#include "strings.h"


int splitUrl( char* url, URLDESCRIPTOR& urlDesc )
{
    ::memset( &urlDesc, 0, sizeof( URLDESCRIPTOR ) );

    if ( !url )
    {
        urlDesc.type = URLTYPE_RELATIVE;

        return 0;
    }

    size_t urlLen = String::GetLength( url );

    if ( urlLen > 4096 )
    {
        return 1;
    }

    int result = 0;

    char* begin = NULL;

    if ( url[ 0 ] == '/' )
    {
        urlDesc.type = URLTYPE_ABSOLUTE;
        begin = url;
    }
    else if ( strstr( url, "://" ) != NULL )
    {
        urlDesc.type = URLTYPE_FULL;

        if ( strstr( url, "http://" ) == url )
        {
            urlDesc.protocol = PROTOCOL_HTTP;
            begin = url + 7;
        }
        else if ( strstr( url, "https://" ) == url )
        {
            urlDesc.protocol = PROTOCOL_HTTPS;
            begin = url + 8;
        }
        else
        {
            urlDesc.protocol = PROTOCOL_UNKNOWN;
            result = 10;
        }
    }
    else
    {
        urlDesc.type = URLTYPE_RELATIVE;
        begin = url;
    }

    if ( urlDesc.type == URLTYPE_FULL )
    {
        char* firstSlash = strchr( begin, '/' );
        bool noPath = firstSlash == NULL;
        size_t hostLength = ( firstSlash ? firstSlash : url + urlLen ) - begin;

        // host
        if ( !hostLength || hostLength > 276 )
        {
            result = 20;
        }

        // path
        char* path = NULL;
        if ( !result && !noPath )
        {
            size_t pathLength = url + urlLen - firstSlash + 1;
            if ( pathLength )
            {
                path = new char[ pathLength ];
                ::memset( path, 0, pathLength );
                ::memcpy( path, firstSlash, pathLength - 1 );
            }
        }

        unsigned short port = 0; // default port number for the current protocol must be used

        if ( !result )
        {
            char* portdel = strchr( begin, ':' );
            if ( portdel < firstSlash )
            {
                if ( portdel )
                {
                    char portString[ 16 ] = { 0 };
                    ::memcpy( portString, begin, portdel - begin );

                    int intPort = Convert::ToInt( portdel + 1 );

                    if ( intPort > ( 1 << 16 ) || !( port = ( uint16_t ) intPort ) )
                    {
                        port = -1;
                        result = 40;
                    }

                    hostLength = portdel - begin;
                }
            }
        }

        if ( !result )
        {
            char* host = new char[ hostLength + 1 ];
            ::memset( host, 0 , hostLength + 1 );
            ::memcpy( host, begin, hostLength );

            urlDesc.host = host;
            urlDesc.port = port;
            urlDesc.path = path;
        }
    }
    else
    {
        urlDesc.path = url;
    }

    if ( result )
    {
        if ( urlDesc.path ) delete urlDesc.path;
        if ( urlDesc.host ) delete urlDesc.host;
    }
    return result;
}

bool isIp( char* host )
{
    char* cp = host;
    char i = 0;
    char c = 0;
    bool isDigit = false;
    bool isDot = false;
    char octet = 0, octets = 1;
    char lastc = 0;
    char digits = 0, digit, dots = 0;
    while ( c = *cp )
    {
        if ( i > 14 ) return false;

        isDigit = c >= '0' && c <= '9';
        isDot = c == '.';

        if ( isDigit )
        {
            digits++;
            digit = c - '0';
            octet = octet * 10 + digit;
            if ( octet > 255 ) return false;
            if ( lastc == '.' )
                octets++;
        }
        else if ( isDot )
        {
            dots++;
            octet = 0;
            if ( !lastc || lastc == '.' ) return false;
        }
        else
            return false;

        lastc = c;

        i++;
        cp++;
    }

    return octets == 4 && dots == 3 && digits <= 12;
}

//#define DELETEUD( ud ) if ( ud.host ) delete host

int Url::Merge( URLDESCRIPTOR& urlDesc, char* url1, char* url2 )
{
    int result = 0;

    URLDESCRIPTOR ud1 = { 0 }, ud2 = { 0 };

    if ( result = ::splitUrl( url1, ud1 ) ) goto cleanup;

    if ( ud1.type == URLTYPE_FULL )
    {
        urlDesc = ud1;
    }
    else
    {
        if ( result = ::splitUrl( url2, ud2 ) ) goto cleanup;

        urlDesc.type = URLTYPE_FULL;
        urlDesc.host = ud2.host;
        urlDesc.port = ud2.port;
        urlDesc.protocol = ud2.protocol;

        if ( ud1.type == URLTYPE_ABSOLUTE || ud2.path == NULL )
        {
            urlDesc.path = ud1.path;
        }
        else
        {
            //if ( ud2.path )
            {
                char* c = strrchr( ud2.path, '/' );
                if ( c )
                {
                    size_t offset = c - ud2.path + 1;

                    size_t path1Length = ud1.path ? String::GetLength( ud1.path ) : 0;
                    size_t pathLength = offset + path1Length;

                    char* path = new char[ pathLength + 1 ];
                    ::memset( path, 0, pathLength + 1 );
                    ::memcpy( path, ud2.path, offset );
                    ::memcpy( path + offset, ud1.path, path1Length );

                    urlDesc.path = path;

                    //if ( ud1.path ) delete ud1.path;
                    //if ( ud2.path ) delete ud2.path;
                    // todo: cleanup
                }
            }
        }
    }

cleanup:

    return result;
}
